/**
 * 中国9号健康产业有限公司
 * Copyright (C) JiuHaoHealth Corporation. All Rights Reserved
 */
package com.jiuhaohealth.generate;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import org.mybatis.generator.api.CommentGenerator;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.Plugin;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.Interface;
import org.mybatis.generator.api.dom.java.JavaVisibility;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.Parameter;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.Element;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.config.JDBCConnectionConfiguration;
import org.mybatis.generator.config.SqlMapGeneratorConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * @author minghui(jackchenmh@gmail.com)
 * @version 2014-3-18 下午5:08:45
 */
public class MysqlPlus extends PluginAdapter implements Plugin {
	static Logger logger = LoggerFactory.getLogger(MysqlPlus.class);


	@Override
	public void initialized(IntrospectedTable introspectedTable) {
		initDatabaseTools(introspectedTable);
		moveXml(introspectedTable);
		super.initialized(introspectedTable);
	}

	/**
	 * 初始化数据库附加工具 TODO:未完成
	 */
	private void initDatabaseTools(IntrospectedTable introspectedTable) {
		JDBCConnectionConfiguration config = context.getJdbcConnectionConfiguration();
		DataBaseTools.getInstanc(config.getDriverClass(), config.getConnectionURL(), config.getUserId(), config.getPassword());
	}

	/**
	 * 移动、备份xml配置文件
	 * @param introspectedTable
	 */
	private void moveXml(IntrospectedTable introspectedTable) {
		SqlMapGeneratorConfiguration sqlmapConfig = context.getSqlMapGeneratorConfiguration();
		String dir = sqlmapConfig.getTargetProject() + "\\" + 
			sqlmapConfig.getTargetPackage().replaceAll("\\.", "\\\\");
		final String fileName = StringUtil.splitLast(introspectedTable.getExampleType(), "\\.").replaceAll("Criteria", "Mapper") + ".xml";
		File file = new File(dir + "\\" + fileName);
		if (file.isFile()) {
			File dest = new File(dir + "\\_" + fileName + "." + new SimpleDateFormat("yyyy-MM-dd_HH_mm_ss").format(new Date()) + ".bak");
			file.renameTo(dest);
		}
	}

	@Override
	public boolean clientGenerated(Interface interfaze, TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
		// 所有client Mapper 类添加统一父接口
		FullyQualifiedJavaType ptype = new FullyQualifiedJavaType("com.jiuhaohealth.base.superpage.MapperSuper");
		interfaze.addImportedType(ptype);
		interfaze.addSuperInterface(ptype);
		return super.clientGenerated(interfaze, topLevelClass, introspectedTable);
	}

	/**
	 *	让PrimaryKey类实现可序列化
	 */
    @Override
	public boolean modelPrimaryKeyClassGenerated(TopLevelClass topLevelClass,
            IntrospectedTable introspectedTable) {
    	// 实体类添加统一父接口
    	for (Field field : topLevelClass.getFields()) {
			field.setVisibility(JavaVisibility.PROTECTED);
		}
		FullyQualifiedJavaType ptype = new FullyQualifiedJavaType("java.io.Serializable");
		topLevelClass.addImportedType(ptype);
		topLevelClass.addSuperInterface(ptype);
        return true;
    }

	/**
	 *
	 */
	@Override
	public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
//		JDBCConnectionConfiguration jdbcConfig = context.getJdbcConnectionConfiguration();
//		
//		for(Method method : topLevelClass.getMethods()) {
//			logger.info("method: {}", method.getName());
//			DataBaseTools.getInstanc().getTable(introspectedTable.getFullyQualifiedTableNameAtRuntime(), new DbConfig(null, null, null));
//			//TODO: 查询数据库给bean上的所有方法加上注释…… 等有时间的时候再做
//			
//		}
		
		// 实体类添加统一父接口
		FullyQualifiedJavaType ptype = new FullyQualifiedJavaType("com.jiuhaohealth.base.superpage.BeanSuper");
		topLevelClass.addImportedType(ptype);
		topLevelClass.addSuperInterface(ptype);
		
		Method methodCopy = new Method();
		methodCopy.setVisibility(JavaVisibility.PUBLIC);
		methodCopy.addJavaDocLine("/** ");
		methodCopy.addJavaDocLine(" * 拷贝，将对象中的字段全部拷贝到子对象中");
		methodCopy.addJavaDocLine(" * @param bean 接收对象的子类");
		methodCopy.addJavaDocLine(" * @return 拷贝完成后的子类");
		methodCopy.addJavaDocLine(" */ ");
		methodCopy.setName("<T extends " + topLevelClass.getType().getShortName() + "> T copy");
		methodCopy.setReturnType(new FullyQualifiedJavaType(""));
		methodCopy.addParameter(new Parameter(new FullyQualifiedJavaType("T"), "bean"));
		
		for (IntrospectedColumn column : introspectedTable.getAllColumns()) {
			String columnName = column.getJavaProperty();
			String methodName = Character.toUpperCase(columnName.charAt(0)) + columnName.substring(1);
			methodCopy.addBodyLine("bean.set" + methodName + "(get" + methodName + "());");
		}
		methodCopy.addBodyLine("return bean;");
		topLevelClass.addMethod(methodCopy);
		
		
		Method methodToString = new Method();
		methodToString.setVisibility(JavaVisibility.PUBLIC);
		methodToString.addJavaDocLine("/** ");
		methodToString.addJavaDocLine(" * 格式化显示");
		methodToString.addJavaDocLine(" */ ");
		methodToString.addAnnotation("@Override");
		methodToString.setName("toString");
		methodToString.setReturnType(FullyQualifiedJavaType.getStringInstance());
//		    	return "year:" + getYear() + ", month:" + getMonth(); 
		methodToString.addBodyLine("return \"{\" + ");
		for (IntrospectedColumn column : introspectedTable.getAllColumns()) {
			String columnName = column.getJavaProperty();
			String methodName = Character.toUpperCase(columnName.charAt(0)) + columnName.substring(1);
			methodToString.addBodyLine("	\", " + columnName + ":\" + get" + methodName + "() + ");
		}
		methodToString.addBodyLine("\"}\";");
		topLevelClass.addMethod(methodToString);
		
		return super.modelBaseRecordClassGenerated(topLevelClass, introspectedTable);
	}

	/**
	 * 字段修改，添加注释
	 */
	@Override
	public boolean modelFieldGenerated(Field field, TopLevelClass topLevelClass, IntrospectedColumn introspectedColumn,
			IntrospectedTable introspectedTable, ModelClassType modelClassType) {
//		field.addJavaDocLine("add field doc");
		return super.modelFieldGenerated(field, topLevelClass, introspectedColumn, introspectedTable, modelClassType);
	}
	
    /**
     * 修改 查询结果对象映射，支持自定义结果对象
     */
	@Override
    public boolean sqlMapResultMapWithoutBLOBsElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
    	String resultClass = introspectedTable.getTableConfiguration().getProperty("resultClass");
    	if(!StringUtil.isEmpty(resultClass)) {
    		final List<Attribute> list = element.getAttributes();
    		for (int i = 0; i < list.size(); i++) {
    			if(list.get(i).getName().equals("type")) {
    				list.set(i, new Attribute("type", resultClass));
    				break;
    			}
    		}
    	}
        return true;
    }
	
    /**
     * 修改client接口，支持自定义结果对象
     */
	@Override
    public boolean clientSelectByExampleWithoutBLOBsMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
    	String resultClass = introspectedTable.getTableConfiguration().getProperty("resultClass");
    	if(!StringUtil.isEmpty(resultClass)) {
    		interfaze.addImportedType(new FullyQualifiedJavaType(resultClass));
    		method.setReturnType(new FullyQualifiedJavaType("List<" + resultClass + ">"));
    	}
        return true;
    }

	/**
	 * 修改client接口，支持自定义结果对象
	 */
	@Override
	public boolean clientSelectByPrimaryKeyMethodGenerated(Method method, Interface interfaze, IntrospectedTable introspectedTable) {
		String resultClass = introspectedTable.getTableConfiguration().getProperty("resultClass");
		if(!StringUtil.isEmpty(resultClass)) {
			interfaze.addImportedType(new FullyQualifiedJavaType(resultClass));
			method.setReturnType(new FullyQualifiedJavaType(resultClass));
		}
		return true;
	}

	//////sqlMap////////付星烨//////支持分表

	private boolean includeCommon(XmlElement element) {
		List<Element> subEl = element.getElements();
		Element et = subEl.get(0);
		et.set(et.getFormattedContent(0));
	    return true;
	}
	
    @Override
	public boolean sqlMapCountByExampleElementGenerated(XmlElement element,
            IntrospectedTable introspectedTable) {
        return includeCommon(element);
    }
	
    @Override
	public boolean sqlMapDeleteByExampleElementGenerated(XmlElement element,
            IntrospectedTable introspectedTable) {
        return includeCommon(element);
    }
    
	@Override
	public boolean sqlMapDeleteByPrimaryKeyElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {

        return includeCommon(element);
	}

    @Override
	public boolean sqlMapInsertElementGenerated(XmlElement element,
            IntrospectedTable introspectedTable) {
    	List<Element> subEl = element.getElements();
		Element et = subEl.get(0);
		String content = et.getFormattedContent(0);
		String tableName = content.substring(12, content.indexOf(" ", 12));
		String newTableName = tableName;
		String newSql = content.replaceAll(tableName, newTableName);
		et.set(newSql);
	    return true;
    }
    @Override
	public boolean sqlMapInsertSelectiveElementGenerated(XmlElement element,
            IntrospectedTable introspectedTable) {
        return includeCommon(element);
    }

    @Override
	public boolean sqlMapUpdateByExampleSelectiveElementGenerated(
            XmlElement element, IntrospectedTable introspectedTable) {
        return includeCommon(element);
    }
    
    @Override
	public boolean sqlMapUpdateByExampleWithBLOBsElementGenerated(
            XmlElement element, IntrospectedTable introspectedTable) {
        return includeCommon(element);
    }

    @Override
	public boolean sqlMapUpdateByExampleWithoutBLOBsElementGenerated(
            XmlElement element, IntrospectedTable introspectedTable) {
        return includeCommon(element);
    }
    
    @Override
	public boolean sqlMapUpdateByPrimaryKeySelectiveElementGenerated(
            XmlElement element, IntrospectedTable introspectedTable) {
        return includeCommon(element);
    }

    @Override
	public boolean sqlMapUpdateByPrimaryKeyWithBLOBsElementGenerated(
            XmlElement element, IntrospectedTable introspectedTable) {
        return includeCommon(element);
    }
    @Override
	public boolean sqlMapUpdateByPrimaryKeyWithoutBLOBsElementGenerated(
            XmlElement element, IntrospectedTable introspectedTable) {
        return includeCommon(element);
    }
    
    @Override
	public boolean sqlMapSelectByPrimaryKeyElementGenerated(XmlElement element,
            IntrospectedTable introspectedTable) {
        return includeCommon(element);
    }
	///////////////////////////////////
	////////  I still believe  ////////
	///////////////////////////////////
	
	@Override  
    public boolean modelExampleClassGenerated(TopLevelClass topLevelClass,  
            IntrospectedTable introspectedTable) {  
        // add field, getter, setter for limit clause  
		// 所有类条件类添加统一父接口
		FullyQualifiedJavaType ptype = new FullyQualifiedJavaType("com.jiuhaohealth.base.superpage.CriteriaSuper");
		topLevelClass.addImportedType(ptype);
		topLevelClass.addSuperInterface(ptype);
        addLimit(topLevelClass, introspectedTable, "limitStart");
        addLimit(topLevelClass, introspectedTable, "limitEnd");
        
        //支持分表的字段
        addField(topLevelClass, 
        		introspectedTable, 
        		"tableOffset", 
        		FullyQualifiedJavaType.getIntInstance(), 
        		"-1");
        return super.modelExampleClassGenerated(topLevelClass,  
                introspectedTable);  
    }  
    @Override  
    public boolean sqlMapSelectByExampleWithoutBLOBsElementGenerated(  
            XmlElement element, IntrospectedTable introspectedTable) {  
        XmlElement isParameterPresenteElemen = element;
        
        ///分表
        List<Element> subEl = element.getElements();
        Element et = subEl.get(3);
        et.set(et.getFormattedContent(0));
      ///分表结束
        
        XmlElement isNotNullElement = new XmlElement("if"); //$NON-NLS-1$  
        isNotNullElement.addAttribute(new Attribute("test", "limitStart>=0")); //$NON-NLS-1$ //$NON-NLS-2$  
        isNotNullElement.addElement(new TextElement(
                "limit ${limitStart} , ${limitEnd}"));
        isParameterPresenteElemen.addElement(isNotNullElement);  
        return super.sqlMapUpdateByExampleWithoutBLOBsElementGenerated(element,  
                introspectedTable);  
    }  
    private void addLimit(TopLevelClass topLevelClass,  
            IntrospectedTable introspectedTable, String name) {  
        CommentGenerator commentGenerator = context.getCommentGenerator();  
        Field field = new Field();  
        field.setVisibility(JavaVisibility.PROTECTED);  
        field.setType(FullyQualifiedJavaType.getIntInstance());  
        field.setName(name);  
        field.setInitializationString("-1");  
        commentGenerator.addFieldComment(field, introspectedTable);  
        topLevelClass.addField(field);  
        char c = name.charAt(0);  
        String camel = Character.toUpperCase(c) + name.substring(1);  
        Method method = new Method();  
        method.setVisibility(JavaVisibility.PUBLIC);  
        method.setName("set" + camel);  
        method.addParameter(new Parameter(FullyQualifiedJavaType  
                .getIntInstance(), name));  
        method.addBodyLine("this." + name + "=" + name + ";");  
        commentGenerator.addGeneralMethodComment(method, introspectedTable);  
        topLevelClass.addMethod(method);  
        method = new Method();  
        method.setVisibility(JavaVisibility.PUBLIC);  
        method.setReturnType(FullyQualifiedJavaType.getIntInstance());  
        method.setName("get" + camel);  
        method.addBodyLine("return " + name + ";");  
        commentGenerator.addGeneralMethodComment(method, introspectedTable);  
        topLevelClass.addMethod(method);  
    }  
    
    /**
     * 向model中加字段
     * */
    private void addField(TopLevelClass topLevelClass,  
            IntrospectedTable introspectedTable, String name, FullyQualifiedJavaType type, String defaultValue) {  
        CommentGenerator commentGenerator = context.getCommentGenerator();  
        Field field = new Field();  
        field.setVisibility(JavaVisibility.PROTECTED);
        field.setType(type);  
        field.setInitializationString(defaultValue);  
        field.setName(name);  
        commentGenerator.addFieldComment(field, introspectedTable);  
        topLevelClass.addField(field);  
        char c = name.charAt(0);  
        String camel = Character.toUpperCase(c) + name.substring(1);  
        Method method = new Method();  
        method.setVisibility(JavaVisibility.PUBLIC);  
        method.setName("set" + camel);  
        method.addParameter(new Parameter(type, name));  
        method.addBodyLine("this." + name + "=" + name + ";");  
        commentGenerator.addGeneralMethodComment(method, introspectedTable);  
        topLevelClass.addMethod(method);  
        method = new Method();  
        method.setVisibility(JavaVisibility.PUBLIC);  
        method.setReturnType(type);  
        method.setName("get" + camel);  
        method.addBodyLine("return " + name + ";");  
        commentGenerator.addGeneralMethodComment(method, introspectedTable);  
        topLevelClass.addMethod(method);  
    }  
    
    /** 
     * This plugin is always valid - no properties are required 
     */  
    public boolean validate(List<String> warnings) {  
        return true;  
    }  
    public static void generate() {  
        
    }  
	
}
